前面已經知道 Kubernetes 裡面幾個重要元件:
Deployment
Service
Ingress
HPA
如果要部署一個 FastAPI Backend,可以先把它們理解成:
Deployment
→ 我要跑幾個 FastAPI Pod
Service
→ 幫這些 Pod 提供固定入口
Ingress
→ 讓外部網路可以透過網域進來
HPA
→ 根據負載自動增加或減少 Pod
所以完整流量大概是:
Internet
│
▼
Ingress
│
▼
Service
│
▼
Deployment
│
▼
Pod
Pod
Pod
而 HPA 則是在旁邊調整 Pod 數量:
Metrics
│
▼
HPA
│
▼
Deployment
│
▼
Pod 數量增加 / 減少
假設 FastAPI 專案:
app/
├── main.py
├── requirements.txt
└── Dockerfile
main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {
"message": "Hello Kubernetes"
}
@app.get("/health")
def health():
return {
"status": "ok"
}
Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD [
"uvicorn",
"main:app",
"--host",
"0.0.0.0",
"--port",
"8000"
]
Build:
docker build -t myregistry.example.com/backend:v1.0 .
Push:
docker push myregistry.example.com/backend:v1.0
現在 Kubernetes 就可以使用:
myregistry.example.com/backend:v1.0
來建立 Pod。
第一份 YAML 可以建立:
deployment.yaml
例如:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-backend
spec:
replicas: 3
selector:
matchLabels:
app: fastapi-backend
template:
metadata:
labels:
app: fastapi-backend
spec:
containers:
- name: fastapi
image: myregistry.example.com/backend:v1.0
ports:
- containerPort: 8000
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
這份設定最重要的是:
kind: Deployment
代表:
我要建立一個 Deployment
這一段:
replicas: 3
代表:
我要維持 3 個 FastAPI Pod
所以 Kubernetes 會建立:
Deployment
│
├── Pod 1
├── Pod 2
└── Pod 3
如果其中一個 Pod 掛掉:
Pod 1 ✓
Pod 2 X
Pod 3 ✓
Deployment 會重新補一個:
Pod 4
讓數量重新回到:
3
這兩段:
selector:
matchLabels:
app: fastapi-backend
以及:
template:
metadata:
labels:
app: fastapi-backend
是在建立一個識別方式。
可以理解成:
這群 Pod 都貼上:
app=fastapi-backend
例如:
Pod 1
app=fastapi-backend
Pod 2
app=fastapi-backend
Pod 3
app=fastapi-backend
Deployment 就知道:
哪些 Pod 是我管理的?
答案就是:
app=fastapi-backend
這個 label 後面 Service 也會用到。
FastAPI 在 Container 裡面執行:
0.0.0.0:8000
所以設定:
ports:
- containerPort: 8000
可以理解成:
這個 Container
主要提供 8000 Port
但是要注意:
這不代表 Internet 已經可以直接存取:
8000
它只是描述 Container 裡面的 Port。
外部流量怎麼進來,後面還需要:
Service
Ingress
前面 Auto Scaling 已經提過:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
可以先簡單理解成:
requests
→ 排程時至少需要多少資源
limits
→ 最多允許使用多少資源
例如:
250m CPU
代表:
0.25 CPU Core
而:
1
代表:
1 CPU Core
Scheduler 可以透過 requests 判斷:
這台 Node 還放不放得下這個 Pod?
而 HPA 在使用 CPU utilization 時,也會受到 CPU request 影響。
FastAPI 已經提供:
GET /health
所以 Deployment 可以加入:
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
以及:
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 20
完整:
containers:
- name: fastapi
image: myregistry.example.com/backend:v1.0
ports:
- containerPort: 8000
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 20
Readiness Probe 解決的是:
這個 Pod 現在可以接 Request 了嗎?
例如 Pod 剛啟動:
Container Start
│
▼
FastAPI Loading
│
▼
Database Connection
│
▼
Application Ready
如果程式還沒完全啟動:
Readiness = Failed
Kubernetes 就不會把流量送進來。
等:
GET /health
→ 200 OK
才會:
Readiness = Ready
然後 Service 才開始把 Request 分配給它。
Liveness Probe 回答的則是:
這個 Application 還活著嗎?
例如:
Process 還在
但 Application 已經卡死
此時:
GET /health
一直失敗。
Kubernetes 可以判斷:
這個 Container 不健康
然後重啟 Container。
所以可以簡單記:
Readiness
→ 可以接流量嗎?
Liveness
→ 還活著嗎?
現在 Deployment 可以寫成:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-backend
spec:
replicas: 3
selector:
matchLabels:
app: fastapi-backend
template:
metadata:
labels:
app: fastapi-backend
spec:
containers:
- name: fastapi
image: myregistry.example.com/backend:v1.0
ports:
- containerPort: 8000
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 20
部署:
kubectl apply -f deployment.yaml
查看:
kubectl get deployment
查看 Pod:
kubectl get pods
可能看到:
fastapi-backend-abc123 Running
fastapi-backend-def456 Running
fastapi-backend-ghi789 Running
目前有:
Pod 1
Pod 2
Pod 3
但是它們可能有自己的 IP:
Pod 1
10.244.1.10
Pod 2
10.244.1.11
Pod 3
10.244.2.5
如果 Pod 掛掉重新建立:
Pod 2 X
新的 Pod 可能變成:
Pod 4
10.244.3.20
所以 Application 不應該直接使用 Pod IP。
這就是:
Service
要解決的問題。
建立:
service.yaml
內容:
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi-backend
ports:
- port: 80
targetPort: 8000
type: ClusterIP
最重要的是:
selector:
app: fastapi-backend
它會找到前面 Deployment 建立的:
app=fastapi-backend
那些 Pod。
所以:
fastapi-service
│
selector: app=fastapi-backend
│
┌──────────┼──────────┐
▼ ▼ ▼
Pod 1 Pod 2 Pod 3
這一段:
ports:
- port: 80
targetPort: 8000
意思是:
Service Port
80
↓
Pod Port
8000
所以 Cluster 裡面的其他服務可以呼叫:
http://fastapi-service
Service 再轉到:
FastAPI :8000
完整:
Client
│
│ :80
▼
Service
│
│ :8000
▼
FastAPI Pod
設定:
type: ClusterIP
代表:
這個 Service
主要提供 Kubernetes Cluster 內部存取
例如 Frontend Pod:
Frontend Pod
│
▼
http://fastapi-service
│
▼
Backend Pods
但是 Internet 外部使用者:
Browser
目前還不能直接透過:
api.example.com
進入。
因此還需要:
Ingress
建立:
ingress.yaml
例如:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 80
這代表:
api.example.com
的 Request:
Internet
│
▼
Ingress
│
▼
fastapi-service
│
▼
FastAPI Pods
前面傳統部署:
server {
server_name api.example.com;
location / {
proxy_pass http://backend;
}
}
Kubernetes 的 Ingress 概念很接近:
api.example.com
│
▼
fastapi-service
所以:
Ingress
並不是很陌生的新概念。
它其實就是在描述:
Domain / Path
應該送去哪一個 Service
例如:
example.com/
→ frontend-service
example.com/api/
→ backend-service
這裡要注意。
建立:
kind: Ingress
只是在告訴 Kubernetes:
我希望流量這樣 Routing
真正執行 HTTP Reverse Proxy 的,是:
Ingress Controller
例如常見:
NGINX Ingress Controller
因此:
Ingress
= Routing 規則
Ingress Controller
= 真正執行規則的程式
可以理解成:
Ingress YAML
│
▼
描述設定
│
▼
Ingress Controller
│
▼
真正接 HTTP Request
Production 通常不會只使用:
http://api.example.com
而是:
https://api.example.com
Ingress 可以搭配 TLS Certificate。
例如概念上:
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls
代表:
api.example.com
使用:
api-tls
這個 Kubernetes Secret 裡面的 TLS Certificate。
實務上也常搭配:
cert-manager
自動管理 Let's Encrypt Certificate。
現在 Backend 已經有:
Deployment
Service
Ingress
接下來加入:
HPA
建立:
hpa.yaml
例如:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: fastapi-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: fastapi-backend
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
這段最重要的是:
scaleTargetRef:
代表:
HPA 要控制誰?
答案:
Deployment
fastapi-backend
例如:
minReplicas: 3
maxReplicas: 10
意思是:
最低
3 Pods
最高
10 Pods
所以平常可能:
3 Pods
流量增加:
5 Pods
更多流量:
8 Pods
尖峰:
10 Pods
但不會超過:
10
流量下降後又可以慢慢回到:
3
這段:
averageUtilization: 60
可以簡單理解成:
希望 Pod 平均 CPU
維持大約 60%
例如:
Pod 1 = 90%
Pod 2 = 85%
Pod 3 = 95%
平均明顯高於:
60%
HPA 就可能提高 replicas。
例如:
3
↓
5
Deployment 再建立新的 Pod。
現在已經有:
deployment.yaml
service.yaml
ingress.yaml
hpa.yaml
它們不是四個互不相關的設定。
而是:
Ingress
│
▼
Service
│
▼
Deployment
│
▼
Pods
另外:
HPA
│
▼
Deployment
所以整體:
Internet
│
▼
Ingress
│
▼
Service
│
▼
Deployment
│
┌───────────┼───────────┐
▼ ▼ ▼
Pod 1 Pod 2 Pod 3
│ │ │
└───────────┼───────────┘
│
▼
Redis / Database
Metrics
│
▼
HPA
│
▼
Deployment
│
▼
replicas 3~10
假設四個檔案:
k8s/
├── deployment.yaml
├── service.yaml
├── ingress.yaml
└── hpa.yaml
可以分別:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
kubectl apply -f hpa.yaml
或者:
kubectl apply -f k8s/
一次套用整個目錄。
查看 Deployment:
kubectl get deployment
查看 Pods:
kubectl get pods
查看 Service:
kubectl get service
查看 Ingress:
kubectl get ingress
查看 HPA:
kubectl get hpa
例如 HPA 可能看到:
NAME TARGETS MINPODS MAXPODS REPLICAS
fastapi-hpa 45%/60% 3 10 3
可以理解成:
目前 CPU
45%
目標
60%
目前 Pods
3
先:
kubectl get pods
如果看到:
CrashLoopBackOff
可以查看:
kubectl logs <pod-name>
例如:
kubectl logs fastapi-backend-abc123
如果需要查看 Kubernetes 認為發生什麼:
kubectl describe pod <pod-name>
例如可以看到:
Image Pull Failed
Readiness Probe Failed
OOMKilled
Scheduling Failed
這些都是實務部署時非常常看的資訊。
FastAPI 通常還需要:
DATABASE_HOST
REDIS_HOST
ENVIRONMENT
API_KEY
JWT_SECRET
Deployment 可以加入:
env:
- name: DATABASE_HOST
value: "mysql-service"
- name: REDIS_HOST
value: "redis-service"
但是像:
Password
API Key
JWT Secret
通常不應直接寫進 Deployment YAML。
Kubernetes 提供:
Secret
來管理敏感設定。
一般設定則可以使用:
ConfigMap
所以架構又可以變成:
ConfigMap
│
▼
Deployment → Pod
▲
│
Secret
可以簡單分成:
ConfigMap
ENVIRONMENT
LOG_LEVEL
DATABASE_HOST
REDIS_HOST
以及:
Secret
DATABASE_PASSWORD
JWT_SECRET
API_KEY
Pod 啟動時再把這些設定注入:
FastAPI Pod
│
├── Application Image
├── ConfigMap
└── Secret
這樣就可以繼續維持前面 Docker 的原則:
相同 Image
+
不同 Environment Configuration
例如:
backend:v1.0
Dev
Staging
Production
全部可以使用同一個 Image。
只換:
ConfigMap
Secret
假設 Kubernetes 裡還有:
redis-service
mysql-service
FastAPI 可以直接透過 Service Name:
REDIS_HOST=redis-service
DATABASE_HOST=mysql-service
因此:
FastAPI Pod
│
├────→ redis-service
│ │
│ ▼
│ Redis
│
└────→ mysql-service
│
▼
MySQL
Backend 不需要知道:
Redis Pod IP
MySQL Pod IP
只需要知道:
Service Name
這也是 Kubernetes Service Discovery 的重要概念。
假設目前:
backend:v1.0
更新後 Build:
backend:v1.1
只要修改 Deployment:
image: myregistry.example.com/backend:v1.1
然後:
kubectl apply -f deployment.yaml
Deployment 就會進行 Rolling Update。
例如:
v1.0
v1.0
v1.0
逐漸:
v1.1
v1.0
v1.0
然後:
v1.1
v1.1
v1.0
最後:
v1.1
v1.1
v1.1
而 Readiness Probe 可以確保:
新的 v1.1 Pod
真正 Ready 之後
才開始接流量
這就能降低部署期間服務中斷的機會。
把目前所有東西串起來:
Internet
│
▼
api.example.com
│
▼
Ingress
│
▼
fastapi-service
│
┌────────────┼────────────┐
▼ ▼ ▼
Pod 1 Pod 2 Pod 3
FastAPI FastAPI FastAPI
│ │ │
└──────┬─────┴─────┬──────┘
│ │
▼ ▼
Redis Database
Deployment 管理:
Pod 1
Pod 2
Pod 3
HPA 管理:
3 Pods
↕
10 Pods
Service 管理:
Request
→ 哪一個 Pod
Ingress 管理:
api.example.com
→ 哪一個 Service
ConfigMap / Secret 管理:
Application Configuration
Docker Image 管理:
Application
+
Runtime
+
Dependencies
所以一次完整的 Kubernetes FastAPI 部署,可以理解成:
Docker Image
│
▼
Deployment
│
▼
Pod
│
▼
Service
│
▼
Ingress
│
▼
Internet
另外:
HPA
↓
Deployment
ConfigMap / Secret
↓
Pod
最後可以用一句話記住:
Deployment
→ 我的 FastAPI 要怎麼跑、跑幾份
Service
→ 這些 FastAPI Pod 要怎麼被穩定找到
Ingress
→ 外面的 HTTP / HTTPS Request 怎麼進來
HPA
→ FastAPI Pod 要根據流量增加還是減少
ConfigMap
→ 一般環境設定
Secret
→ 密碼、Token、Key 等敏感設定
到這裡,Kubernetes 已經不再只是:
很多陌生 YAML
而是每一份 YAML 都是在解決之前已經遇過的部署問題。
從最早:
SSH Server
→ 啟動 FastAPI
一路演進成:
Build Docker Image
│
▼
Container Registry
│
▼
Kubernetes Deployment
│
▼
Pods
│
▼
Service
│
▼
Ingress
│
▼
User
而整個系統還能透過 HPA 自動擴縮,透過 Health Check 自動判斷 Pod 狀態,並利用 Rolling Update 逐步完成版本更新。
這就是從傳統單機部署,走到 Container 與 Kubernetes 部署之後,整體部署方式最大的轉變。